#include "wx/wxprec.h"

#if wxUSE_POPUPWIN

#include "wx/popupwin.h"
#include <gdk/gdk.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "wx/gtk/private.h"
#include "wx/gtk/win_gtk.h"

extern "C" {
  static gint gtk_popup_button_press( GtkWidget *widget, GdkEvent *gdk_event, wxPopupWindow* win ) {
    GtkWidget *child = gtk_get_event_widget( gdk_event );
    /* Ignore events sent out before we connected to the signal */
    if( win->m_time >= ( ( GdkEventButton* )gdk_event )->time ) {
      return FALSE;
    }
    if( child != widget ) {
      while( child ) {
        if( child == widget ) {
          return FALSE;
        }
        child = child->parent;
      }
    }
    wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
    event.SetEventObject( win );
    ( void )win->GetEventHandler()->ProcessEvent( event );
    return TRUE;
  }
}

//-----------------------------------------------------------------------------
// "focus" from m_window
//-----------------------------------------------------------------------------

extern "C" {
  static gint gtk_dialog_focus_callback( GtkWidget *widget, GtkDirectionType ( d ), wxWindow *( win ) ) {
    if( g_isIdle ) {
      wxapp_install_idle_handler();
    }
    /* This disables GTK's tab traversal */
    return TRUE;
  }
}

//-----------------------------------------------------------------------------
// "delete_event"
//-----------------------------------------------------------------------------

extern "C" {
  bool gtk_dialog_delete_callback( GtkWidget *( widget ), GdkEvent *( event ), wxPopupWindow *win ) {
    if( g_isIdle ) {
      wxapp_install_idle_handler();
    }
    if( win->IsEnabled() ) {
      win->Close();
    }
    return TRUE;
  }
}

//-----------------------------------------------------------------------------
// "realize" from m_widget
//-----------------------------------------------------------------------------

/* we cannot MWM hints and icons before the widget has been realized,
   so we do this directly after realization */

extern "C" {
  static gint
  gtk_dialog_realized_callback( GtkWidget * ( widget ), wxPopupWindow *win ) {
    if( g_isIdle ) {
      wxapp_install_idle_handler();
    }
    /* all this is for Motif Window Manager "hints" and is supposed to be
       recognized by other WM as well. not tested. */
    long decor = ( long ) GDK_DECOR_BORDER;
    long func = ( long ) GDK_FUNC_MOVE ;
    gdk_window_set_decorations( win->m_widget->window, ( GdkWMDecoration )decor );
    gdk_window_set_functions( win->m_widget->window, ( GdkWMFunction )func );
    gtk_window_set_resizable( GTK_WINDOW( win->m_widget ), FALSE );
    return FALSE;
  }
}

//-----------------------------------------------------------------------------
// InsertChild for wxPopupWindow
//-----------------------------------------------------------------------------

/* Callback for wxFrame. This very strange beast has to be used because
   C++ has no virtual methods in a constructor. We have to emulate a
   virtual function here as wxWidgets requires different ways to insert
   a child in container classes. */

static void wxInsertChildInDialog( wxPopupWindow* parent, wxWindow* child ) {
  gtk_pizza_put( GTK_PIZZA( parent->m_wxwindow ),
                 GTK_WIDGET( child->m_widget ),
                 child->m_x,
                 child->m_y,
                 child->m_width,
                 child->m_height );
  if( parent->HasFlag( wxTAB_TRAVERSAL ) ) {
    /* we now allow a window to get the focus as long as it
       doesn't have any children. */
    GTK_WIDGET_UNSET_FLAGS( parent->m_wxwindow, GTK_CAN_FOCUS );
  }
}

//-----------------------------------------------------------------------------
// wxPopupWindow
//-----------------------------------------------------------------------------

BEGIN_EVENT_TABLE( wxPopupWindow, wxPopupWindowBase )
#ifdef __WXUNIVERSAL__
  EVT_SIZE( wxPopupWindow::OnSize )
#endif
END_EVENT_TABLE()

wxPopupWindow::~wxPopupWindow() {
}

bool wxPopupWindow::Create( wxWindow *parent, int style ) {
  m_needParent = false;
  if( !PreCreation( parent, wxDefaultPosition, wxDefaultSize ) ||
      !CreateBase( parent, -1, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT( "popup" ) ) ) {
    wxFAIL_MSG( wxT( "wxPopupWindow creation failed" ) );
    return false;
  }
  // Unlike windows, top level windows are created hidden by default.
  m_isShown = false;
  // All dialogs should really have this style
  m_windowStyle |= wxTAB_TRAVERSAL;
  m_insertCallback = ( wxInsertChildFunction ) wxInsertChildInDialog;
  m_widget = gtk_window_new( GTK_WINDOW_POPUP );
  if( ( m_parent ) && ( GTK_IS_WINDOW( m_parent->m_widget ) ) ) {
    gtk_window_set_transient_for( GTK_WINDOW( m_widget ), GTK_WINDOW( m_parent->m_widget ) );
  }
  GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
  g_signal_connect( m_widget, "delete_event",
                    G_CALLBACK( gtk_dialog_delete_callback ), this );
  m_wxwindow = gtk_pizza_new();
  gtk_widget_show( m_wxwindow );
  GTK_WIDGET_UNSET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
  gtk_container_add( GTK_CONTAINER( m_widget ), m_wxwindow );
  if( m_parent ) {
    m_parent->AddChild( this );
  }
  PostCreation();
  /*  we cannot set MWM hints  before the widget has
      been realized, so we do this directly after realization */
  g_signal_connect( m_widget, "realize",
                    G_CALLBACK( gtk_dialog_realized_callback ), this );
  // disable native tab traversal
  g_signal_connect( m_widget, "focus",
                    G_CALLBACK( gtk_dialog_focus_callback ), this );
  m_time = gtk_get_current_event_time();
  g_signal_connect( m_widget, "button_press_event",
                    G_CALLBACK( gtk_popup_button_press ), this );
  return true;
}

void wxPopupWindow::DoMoveWindow( int ( x ), int ( y ), int ( width ), int ( height ) ) {
  wxFAIL_MSG( wxT( "DoMoveWindow called for wxPopupWindow" ) );
}

void wxPopupWindow::DoSetSize( int x, int y, int width, int height, int sizeFlags ) {
  wxASSERT_MSG( ( m_widget != NULL ), wxT( "invalid dialog" ) );
  wxASSERT_MSG( ( m_wxwindow != NULL ), wxT( "invalid dialog" ) );
  if( m_resizing ) {
    return;  /* I don't like recursions */
  }
  m_resizing = true;
  int old_x = m_x;
  int old_y = m_y;
  int old_width = m_width;
  int old_height = m_height;
  if( ( sizeFlags & wxSIZE_ALLOW_MINUS_ONE ) == 0 ) {
    if( x != -1 ) {
      m_x = x;
    }
    if( y != -1 ) {
      m_y = y;
    }
    if( width != -1 ) {
      m_width = width;
    }
    if( height != -1 ) {
      m_height = height;
    }
  } else {
    m_x = x;
    m_y = y;
    m_width = width;
    m_height = height;
  }
  /*
      if ((sizeFlags & wxSIZE_AUTO_WIDTH) == wxSIZE_AUTO_WIDTH) {
          if (width == -1) m_width = 80;
      }

      if ((sizeFlags & wxSIZE_AUTO_HEIGHT) == wxSIZE_AUTO_HEIGHT) {
         if (height == -1) m_height = 26;
      }
  */
  int minWidth = GetMinWidth(),
      minHeight = GetMinHeight(),
      maxWidth = GetMaxWidth(),
      maxHeight = GetMaxHeight();
  if( ( minWidth != -1 ) && ( m_width < minWidth ) ) {
    m_width = minWidth;
  }
  if( ( minHeight != -1 ) && ( m_height < minHeight ) ) {
    m_height = minHeight;
  }
  if( ( maxWidth != -1 ) && ( m_width > maxWidth ) ) {
    m_width = maxWidth;
  }
  if( ( maxHeight != -1 ) && ( m_height > maxHeight ) ) {
    m_height = maxHeight;
  }
  if( ( m_x != -1 ) || ( m_y != -1 ) ) {
    if( ( m_x != old_x ) || ( m_y != old_y ) ) {
      /* we set the position here and when showing the dialog
         for the first time in idle time */
      // Where does that happen in idle time? I do not see it anywhere - MR
      gtk_window_move( GTK_WINDOW( m_widget ), m_x, m_y );
    }
  }
  if( ( m_width != old_width ) || ( m_height != old_height ) ) {
    gtk_widget_set_size_request( m_widget, m_width, m_height );
    /* actual resizing is deferred to GtkOnSize in idle time and
       when showing the dialog */
    m_sizeSet = false;
  }
  m_resizing = false;
}

void wxPopupWindow::GtkOnSize() {
  if( m_sizeSet ) {
    return;
  }
  if( !m_wxwindow ) {
    return;
  }
  gint flag = GDK_HINT_MAX_SIZE | GDK_HINT_MIN_SIZE;
  GdkGeometry geom;
  geom.min_width = m_width;
  geom.min_height = m_height;
  geom.max_width = m_width;
  geom.max_height = m_height;
  gtk_window_set_geometry_hints( GTK_WINDOW( m_widget ),
                                 ( GtkWidget* ) NULL,
                                 &geom,
                                 ( GdkWindowHints ) flag );
  m_sizeSet = true;
  wxSizeEvent event( wxSize( m_width, m_height ), GetId() );
  event.SetEventObject( this );
  GetEventHandler()->ProcessEvent( event );
}

void wxPopupWindow::OnInternalIdle() {
  if( !m_sizeSet && GTK_WIDGET_REALIZED( m_wxwindow ) ) {
    GtkOnSize();
  }
  wxWindow::OnInternalIdle();
}

bool wxPopupWindow::Show( bool show ) {
  if( show && !m_sizeSet ) {
    /* by calling GtkOnSize here, we don't have to call
       either after showing the frame, which would entail
       much ugly flicker nor from within the size_allocate
       handler, because GTK 1.1.X forbids that. */
    GtkOnSize();
  }
  bool ret = wxWindow::Show( show );
  return ret;
}

#endif // wxUSE_POPUPWIN
